#include "util.h"


namespace yuechuan {
    yuechuan::Logger::ptr g_logger = YUECHUAN_LOG_NAME("system");

    pid_t GetThreadId() {
        return syscall(SYS_gettid);
    }

    uint32_t GetFiberId() {
        return Fiber::GetFiberId();
    }


    void Backtrace(std::vector<std::string> &bt, int size, int skip) {
        /// 默认是的调用栈是线程上的栈，其占用空间比较大，要实现协程中轻量的栈，这里使用多重指针来缩短占用空间
        void **array = (void **) malloc((sizeof(void *) * size));
        size_t s = ::backtrace(array, size);
        char **string = backtrace_symbols(array, size);
        if (string == nullptr) {
            YUECHUAN_LOG_INFO(g_logger) << "backtrace_symbols error";
            free(string);
            free(array);
            return;
        }
        for (size_t i = skip; i < s; ++i) {
            bt.emplace_back(string[i]);
        }
        free(string);
        free(array);
    }


    std::string BacktraceToString(int size, int skip, const std::string &prefix) {
        std::vector<std::string> bt;
        Backtrace(bt, size, skip);
        std::stringstream ss;
        for (auto &i : bt) {
            ss << prefix << i << std::endl;
        }
        return ss.str();
    }

    static int __mkdir(const char *dirname) {
        if (access(dirname, F_OK) == 0) {
            return 0;
        }
        return mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
    }

    static int __lstat(const char *file, struct stat *st = nullptr) {
        struct stat lst;
        int ret = lstat(file, &lst);
        if (st) {
            *st = lst;
        }
        return ret;
    }

    std::string FSUtil::Dirname(const std::string &filename) {
        if (filename.empty()) {
            return ".";
        }
        auto pos = filename.rfind('/');
        if (pos == 0) {
            return "/";
        } else if (pos == std::string::npos) {
            return ".";
        } else {
            return filename.substr(0, pos);
        }
    }

    bool FSUtil::Mkdir(const std::string &dirname) {
        if (__lstat(dirname.c_str()) == 0) {
            return true;
        }
        char *path = strdup(dirname.c_str());
        char *ptr = strchr(path + 1, '/');
        do {
            for (; ptr; *ptr = '/', ptr = strchr(ptr + 1, '/')) {
                *ptr = '\0';
                if (__mkdir(path) != 0) {
                    break;
                }
            }
            if (ptr != nullptr) {
                break;
            } else if (__mkdir(path) != 0) {
                break;
            }
            free(path);
            return true;
        } while (false);
        free(path);
        return false;
    }

    bool FSUtil::OpenForWrite(std::ofstream &ofs, const std::string &filename, std::ios_base::openmode mode) {
        ofs.open(filename.c_str(), mode);
        if (!ofs.is_open()) {
            std::string dir = Dirname(filename);
            Mkdir(dir);
            ofs.open(filename.c_str(), mode);
        }
        return ofs.is_open();
    }

    uint64_t GetCurrentTimeMS() {
        timeval time{};
        gettimeofday(&time, nullptr);
        uint64_t rt_time = time.tv_sec * 1000ul + time.tv_usec / 1000;
        return rt_time;
    }

    uint64_t GetCurrentTimeUS() {
        timeval time{};
        gettimeofday(&time, nullptr);
        return time.tv_usec + time.tv_sec * 1000 * 1000ul;
    }

    std::string ToUpper(const std::string &name) {
        std::string rt = name;
        std::transform(rt.begin(), rt.end(), rt.begin(), ::toupper);
        return rt;
    }

    std::string ToLower(const std::string &name) {
        std::string rt = name;
        std::transform(rt.begin(), rt.end(), rt.begin(), ::tolower);
        return rt;
    }

    std::string Time2Str(time_t ts, const std::string &format) {
        struct tm tm;
        localtime_r(&ts, &tm);
        char buf[64];
        strftime(buf, sizeof(buf), format.c_str(), &tm);
        return buf;
    }

    time_t Str2Time(const char *str, const char *format) {
        struct tm tm;
        memset(&tm, 0, sizeof(tm));
        if (!strptime(str, format, &tm)) {
            return 0;
        }
        return mktime(&tm);
    }

    std::string StringUtil::Format(const char *fmt, ...) {
        va_list ap;
        va_start(ap, fmt);
        auto v = Formatv(fmt, ap);
        va_end(ap);
        return v;
    }

    std::string StringUtil::Formatv(const char *fmt, va_list ap) {
        char *buff = nullptr;
        auto len = vasprintf(&buff, fmt, ap);
        if (len == -1) {
            return "";
        }
        std::string ret(buff, len);
        free(buff);
        return ret;
    }

    static const char uri_chars[256] = {
            /* 0 */
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
            /* 64 */
            0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
            0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0,
            /* 128 */
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            /* 192 */
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    };

    static const char xdigit_chars[256] = {
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
            0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    };

#define CHAR_IS_UNRESERVED(c)           \
    (uri_chars[(unsigned char)(c)])

//-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~

    std::string StringUtil::UrlEncode(const std::string &str, bool space_as_plus) {
        static const char *hexdigits = "0123456789ABCDEF";
        std::string *ss = nullptr;
        const char *end = str.c_str() + str.length();
        for (const char *c = str.c_str(); c < end; ++c) {
            if (!CHAR_IS_UNRESERVED(*c)) {
                if (!ss) {
                    ss = new std::string;
                    ss->resize(str.size() * 1.2);
                    ss->append(str.c_str(), c - str.c_str());
                }
                if (*c == ' ' && space_as_plus) {
                    ss->append(1, '+');
                } else {
                    ss->append(1, '%');
                    ss->append(1, hexdigits[(uint8_t) * c >> 4]);
                    ss->append(1, hexdigits[*c & 0xf]);
                }
            } else if (ss) {
                ss->append(1, *c);
            }
        }
        if (!ss) {
            return str;
        } else {
            std::string rt = *ss;
            delete ss;
            return rt;
        }
    }

    std::string StringUtil::UrlDecode(const std::string &str, bool space_as_plus) {
        std::string *ss = nullptr;
        const char *end = str.c_str() + str.length();
        for (const char *c = str.c_str(); c < end; ++c) {
            if (*c == '+' && space_as_plus) {
                if (!ss) {
                    ss = new std::string;
                    ss->append(str.c_str(), c - str.c_str());
                }
                ss->append(1, ' ');
            } else if (*c == '%' && (c + 2) < end && isxdigit(*(c + 1)) && isxdigit(*(c + 2))) {
                if (!ss) {
                    ss = new std::string;
                    ss->append(str.c_str(), c - str.c_str());
                }
                ss->append(1, (char) (xdigit_chars[(int) *(c + 1)] << 4 | xdigit_chars[(int) *(c + 2)]));
                c += 2;
            } else if (ss) {
                ss->append(1, *c);
            }
        }
        if (!ss) {
            return str;
        } else {
            std::string rt = *ss;
            delete ss;
            return rt;
        }
    }

    std::string StringUtil::Trim(const std::string &str, const std::string &delimit) {
        auto begin = str.find_first_not_of(delimit);
        if (begin == std::string::npos) {
            return "";
        }
        auto end = str.find_last_not_of(delimit);
        return str.substr(begin, end - begin + 1);
    }

    std::string StringUtil::TrimLeft(const std::string &str, const std::string &delimit) {
        auto begin = str.find_first_not_of(delimit);
        if (begin == std::string::npos) {
            return "";
        }
        return str.substr(begin);
    }

    std::string StringUtil::TrimRight(const std::string &str, const std::string &delimit) {
        auto end = str.find_last_not_of(delimit);
        if (end == std::string::npos) {
            return "";
        }
        return str.substr(0, end);
    }

    std::wstring StringUtil::StringToWString(const std::string &s) {
        std::string str_locale = setlocale(LC_ALL, "");
        const char *chSrc = s.c_str();
        size_t n_dest_size = mbstowcs(NULL, chSrc, 0) + 1;
        wchar_t *wch_dest = new wchar_t[n_dest_size];
        wmemset(wch_dest, 0, n_dest_size);
        mbstowcs(wch_dest, chSrc, n_dest_size);
        std::wstring wstr_result = wch_dest;
        delete[]wch_dest;
        setlocale(LC_ALL, str_locale.c_str());
        return wstr_result;
    }

    std::string StringUtil::WStringToString(const std::wstring &ws) {

        std::string str_locale = setlocale(LC_ALL, "");
        const wchar_t *wch_src = ws.c_str();
        size_t n_dest_size = wcstombs(NULL, wch_src, 0) + 1;
        char *ch_dest = new char[n_dest_size];
        memset(ch_dest, 0, n_dest_size);
        wcstombs(ch_dest, wch_src, n_dest_size);
        std::string str_result = ch_dest;
        delete[]ch_dest;
        setlocale(LC_ALL, str_locale.c_str());
        return str_result;
    }

    std::string GetHostName() {
        std::shared_ptr<char> host(new char[512], yuechuan::delete_array<char>);
        memset(host.get(), 0, 512);
        gethostname(host.get(), 511);
        return host.get();
    }

    in_addr_t GetIPv4Inet() {
        struct ifaddrs *ifas = nullptr;
        struct ifaddrs *ifa = nullptr;

        in_addr_t localhost = inet_addr("127.0.0.1");
        if (getifaddrs(&ifas)) {
            YUECHUAN_LOG_ERROR(g_logger) << "getifaddrs errno = " << errno << " " << strerror(errno);
            return localhost;
        }
        in_addr_t ipv4 = localhost;

        for (ifa = ifas; ifa && ifa->ifa_addr; ifa = ifa->ifa_next) {
            if (ifa->ifa_addr->sa_family != AF_INET) {
                continue;
            }
            if (!strncasecmp(ifa->ifa_name, "lo", 2)) {
                continue;
            }
            ipv4 = ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr.s_addr;
            if (ipv4 == localhost) {
                continue;
            }
        }
        if (ifas != nullptr) {
            freeifaddrs(ifas);
        }
        return ipv4;
    }

    std::string _GetIPv4() {
        std::shared_ptr<char> ipv4(new char[INET_ADDRSTRLEN], yuechuan::delete_array<char>);
        memset(ipv4.get(), 0, INET_ADDRSTRLEN);
        auto ia = GetIPv4Inet();
        inet_ntop(AF_INET, &ia, ipv4.get(), INET_ADDRSTRLEN);
        return ipv4.get();
    }

    std::string GetIPv4() {
        static const std::string ip = _GetIPv4();
        return ip;
    }

    bool YamlToJson(const YAML::Node &ynode, Json::Value &jnode) {
        try {
            if (ynode.IsScalar()) {
                Json::Value v(ynode.Scalar());
                jnode.swapPayload(v);
                return true;
            }
            if (ynode.IsSequence()) {
                for (size_t i = 0; i < ynode.size(); ++i) {
                    Json::Value v;
                    if (YamlToJson(ynode[i], v)) {
                        jnode.append(v);
                    } else {
                        return false;
                    }
                }
            } else if (ynode.IsMap()) {
                for (auto it = ynode.begin(); it != ynode.end(); ++it) {
                    Json::Value v;
                    if (YamlToJson(it->second, v)) {
                        jnode[it->first.Scalar()] = v;
                    } else {
                        return false;
                    }
                }
            }
        } catch (...) {
            return false;
        }
        return true;
    }

    bool JsonToYaml(const Json::Value &jnode, YAML::Node &ynode) {
        try {
            if (jnode.isArray()) {
                for (int i = 0; i < (int) jnode.size(); ++i) {
                    YAML::Node n;
                    if (JsonToYaml(jnode[i], n)) {
                        ynode.push_back(n);
                    } else {
                        return false;
                    }
                }
            } else if (jnode.isObject()) {
                for (auto it = jnode.begin(); it != jnode.end(); ++it) {
                    YAML::Node n;
                    if (JsonToYaml(*it, n)) {
                        ynode[it.name()] = n;
                    } else {
                        return false;
                    }
                }
            } else {
                ynode = jnode.asString();
            }
        } catch (...) {
            return false;
        }
        return true;
    }


    int8_t TypeUtil::ToChar(const char *str) {
        if (str == nullptr) {
            return 0;
        }
        return str[0];
    }

    int8_t TypeUtil::ToChar(const std::string &str) {
        if (str.empty()) {
            return 0;
        }
        return *str.begin();
    }

    double TypeUtil::Atof(const char *str) {
        if (str == nullptr) {
            return 0;
        }
        return atof(str);
    }

    double TypeUtil::Atof(const std::string &str) {
        if (str.empty()) {
            return 0;
        }
        return atof(str.c_str());
    }

    int64_t TypeUtil::Atoi(const char *str) {
        if (str == nullptr) {
            return 0;
        }
        return atoi(str);
    }

    int64_t TypeUtil::Atoi(const std::string &str) {
        if (str.empty()) {
            return 0;
        }
        return atoi(str.c_str());
    }

} // namespace yuechuan


/*
 * 5/3
 * 添加了TypeUtil 支持string，char*类型转换为int， double
 * 添加了YamlToJson函数
 * 添加了JsonToYaml函数
 */
